package org.oc.qaq.service.Impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.oc.qaq.config.OnlyOfficeConfig;
import org.oc.qaq.constant.FileStatusConstant;
import org.oc.qaq.constant.MessageConstant;
import org.oc.qaq.context.UserContext;
import org.oc.qaq.dto.FileDTO;
import org.oc.qaq.dto.FilePageQueryDTO;
import org.oc.qaq.entity.FileEntity;
import org.oc.qaq.entity.Tag;
import org.oc.qaq.exception.FileNotFoundException;
import org.oc.qaq.exception.UnsupportedFileTypeException;
import org.oc.qaq.mapper.FileMapper;
import org.oc.qaq.mapper.RoleMapper;
import org.oc.qaq.result.PageResult;
import org.oc.qaq.result.Result;
import org.oc.qaq.service.*;
import org.oc.qaq.utils.AliOssUtil;
import org.oc.qaq.vo.FileVO;
import org.oc.qaq.vo.TagVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Slf4j
@Service
public class FileServiceImpl implements FileService {

    @Autowired
    private FileMapper fileMapper;

    @Autowired
    private AliOssUtil aliOssUtil;

    @Autowired
    private OnlyOfficeService onlyOfficeService;

    @Autowired
    private TagService tagService;

    @Autowired
    private DocumentService documentService;

    @Autowired
    private RoleMapper roleMapper;

    /**
     * 上传文件方法。
     * 支持新增文件或更新已有文件，并处理文件与标签的关联。
     *
     * @param file   要上传的 MultipartFile 文件对象
     * @param tagIds 可选的标签 ID 列表，用于将文件与标签进行关联
     * @return Result<String> 返回操作结果（成功或失败信息）
     * @throws UnsupportedFileTypeException 如果文件类型不支持，抛出异常
     * @throws IOException                  如果读取文件流或上传过程中发生 I/O 异常
     */
    public Result<String> upload(MultipartFile file, List<Long> tagIds)
            throws UnsupportedFileTypeException, IOException {
        if (file != null) {
            String originalFilename = file.getOriginalFilename();
            if (originalFilename != null) {
                // 解析原始文件名，获取文件名主体和扩展名
                String[] fileNames = originalFilename.split("\\.");
                String fileName = fileNames[0];           // 文件名主体
                String ext = fileNames[fileNames.length - 1]; // 文件扩展名

                // 查询是否已存在同名文件
                FileEntity fileEntity = fileMapper.selectFileByName(fileName);
                if (fileEntity == null) {
                    // 若不存在，则创建新文件实体
                    String uuid = UUID.randomUUID().toString();
                    String fileFullName = uuid + "." + ext;
                    fileEntity = FileEntity.builder()
                            .id(uuid)
                            .name(fileName)
                            .type(ext)
                            .build();

                    // 上传文件至 OSS 并保存 URL
                    String uploaded_url = aliOssUtil.upload(file.getBytes(), ext + "/" + fileFullName);
                    fileEntity.setUrl(uploaded_url);

                    // 插入新文件记录到数据库
                    fileMapper.insert(fileEntity);

                    // 建立文件与标签的关联
                    if (tagIds != null && !tagIds.isEmpty()) {
                        fileMapper.insertFileTags(fileEntity.getId(), tagIds);
                    } else {
                        // 如果未指定标签，则使用当前用户的默认标签
                        String currUserId = UserContext.getCurrentId();
                        List<Tag> tags = tagService.findTagsByUserId(currUserId);
                        if (tags != null && !tags.isEmpty()) {
                            fileMapper.insertFileTag(fileEntity.getId(), tags.get(0).getId());
                        }
                    }

                } else {
                    // 如果文件已存在，则更新文件内容及 URL
                    String fileFullName = fileEntity.getId() + "." + ext;
                    String uploaded_url = aliOssUtil.upload(file.getBytes(), ext + "/" + fileFullName);
                    fileEntity.setUrl(uploaded_url);
                    fileMapper.update(fileEntity);

                    // 清除旧的标签关联
                    fileMapper.deleteFileTags(fileEntity.getId());
                    if (tagIds != null && !tagIds.isEmpty()) {
                        fileMapper.insertFileTags(fileEntity.getId(), tagIds);
                    }
                }

                // 将文件信息同步到文档服务中
                documentService.asyncSave(fileEntity);

                return Result.success(); // 返回上传成功结果
            }
        }
        return Result.error(MessageConstant.UPLOAD_FAILED); // 文件为空时返回失败信息
    }

    /**
     * 下载文件方法。
     * 根据文件 ID 从数据库查询文件信息，并从 OSS 下载对应文件流返回给客户端。
     *
     * @param id 文件唯一标识符
     * @return ResponseEntity<byte [ ]> 返回 HTTP 响应，包含文件字节流及响应头信息
     * @throws IOException 如果下载文件过程中发生 I/O 异常
     */
    @Override
    public ResponseEntity<byte[]> download(String id) throws IOException {
        // 查询文件信息
        FileEntity file = fileMapper.selectFileById(id);

        if (file == null) {
            // 若文件不存在，抛出异常
            throw new FileNotFoundException(MessageConstant.FILE_NOT_FOUND);
        }

        // 获取文件名相关信息
        String name = file.getName();             // 文件名主体
        String ext = file.getType();              // 文件扩展名
        String fullName = file.getId() + "." + ext; // 文件在 OSS 中的完整名称
        String originalName = name + "." + ext;   // 原始文件名（用于响应头）

        // 从 OSS 下载文件流
        try (InputStream inputStream = aliOssUtil.download(fullName, name, ext)) {
            // 将输入流转为字节数组
            byte[] fileBytes = inputStream.readAllBytes();

            // 构建并返回 HTTP 响应，包含文件内容及下载头信息
            return ResponseEntity.ok()
                    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + originalName)
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(fileBytes);
        }
    }


    @Override
    @Transactional
    public OnlyOfficeConfig createOrUpdate(FileDTO fileDTO) throws IOException {
        FileEntity file = fileMapper.selectFileByName(fileDTO.getName());
        String suffix = "." + fileDTO.getType();

        if (file == null) {
            file = new FileEntity();
            file.setId(UUID.randomUUID().toString());
            try (InputStream templateIn = getClass().getResourceAsStream("/templates/empty" + suffix)) {
                String fileUrl = aliOssUtil.upload(templateIn.readAllBytes(),
                        fileDTO.getType() + '/' + file.getId() + suffix);
                file.setUrl(fileUrl);
            }
            BeanUtils.copyProperties(fileDTO, file);
            fileMapper.insert(file);
        } else {
            BeanUtils.copyProperties(fileDTO, file);
            fileMapper.update(file);
        }

        // 清除旧的标签关系
        fileMapper.deleteFileTags(file.getId());

        // 添加新的标签关系（tagIds）
        if (fileDTO.getTags() != null && !fileDTO.getTags().isEmpty()) {
            fileMapper.insertFileTags(file.getId(), fileDTO.getTags());
        }

        return onlyOfficeService.LoadConfig(file.getId());

    }

    @Override
    public void rename(String newName, String id) {
        FileEntity file = fileMapper.selectFileById(id);
        if (file == null) {
            throw new FileNotFoundException(MessageConstant.FILE_NOT_FOUND);
        }
        file.setName(newName);
        fileMapper.update(file);
    }

    @Override
    public PageResult<?> listFiles(FilePageQueryDTO queryDTO) {
        String userId = UserContext.getCurrentId();
        List<Long> roleIds = roleMapper.getRoleIdsByUserId(userId);

        PageHelper.startPage(queryDTO.getPageNum(), queryDTO.getPageSize());

        Page<FileVO> files;

        boolean isAdmin = roleIds.contains(0L);
        boolean isRecycleQuery = Boolean.TRUE.equals(queryDTO.getDeleted());

        if (isAdmin) {
            // 管理员，查询所有或回收站文件
            files = fileMapper.selectAllFiles(queryDTO.getKeyword(), isRecycleQuery);
        } else {
            // 普通用户，查询其角色对应标签下的文件
            files = fileMapper.selectFilesByUserIdAndTags(
                    queryDTO.getKeyword(),
                    userId,
                    queryDTO.getTags(),
                    roleIds,
                    isRecycleQuery
            );
        }

        // 设置标签信息
        if (!files.isEmpty()) {
            List<String> fileIds = files.stream().map(FileVO::getId).toList();
            Map<String, List<TagVO>> tagMap = tagService.findTagsByFileIds(fileIds);
            files.forEach(file -> file.setTags(tagMap.getOrDefault(file.getId(), Collections.emptyList())));
        }

        return new PageResult(files.getTotal(), files.getResult());
    }


    @Override
    public FileEntity getOneById(String fileId) {
        FileEntity file = fileMapper.selectFileById(fileId);
        if (file == null) {
            throw new FileNotFoundException(MessageConstant.FILE_NOT_FOUND);
        }
        return file;
    }

    @Override
    public Result<String> delete(String id) {
        FileEntity file = fileMapper.selectFileById(id);
        if (file == null) {
            throw new FileNotFoundException(MessageConstant.FILE_NOT_FOUND);
        }

        tagService.tagFile(id, FileStatusConstant.DELETED);

        documentService.deleteByDocId(id);

        return Result.success();
    }

    @Override
    public Result<String> deleteCompletely(String id) {
        tagService.deleteTagByFileId(id);
        fileMapper.deleteById(id);
        documentService.delete(id);
        return Result.success();
    }

    @Override
    public void changeTag(String fileId, Long tagId) {
        tagService.deleteTagByFileId(fileId);
        tagService.tagFile(fileId, tagId);
    }

    @Override
    public Result<String> restore(String id) {
        tagService.removeDeleteTag(id);
        return Result.success();
    }

    @Override
    public void updateFileUrl(String fileId, String ossUrl) {
        FileEntity file = fileMapper.selectFileById(fileId);
        if (file == null) {
            throw new FileNotFoundException(MessageConstant.FILE_NOT_FOUND);
        }
        file.setUrl(ossUrl);
        fileMapper.update(file);
    }

}
